/**
 * @file managefifo.c
 * @brief Implementa le funzioni definite in managerfifo.h.
 *
 * Realizza un listener per ricevere le richieste di nuove sessioni fifo e implementa
 * le funzioni per fornire i file descriptor per comunicare con le sessioni.
 *
 * @date Mar 11, 2012
 * @author Francesco Corvino
 */

#include "managerfifo.h"	/** utilizzo delle definizioni e delle interfacce */
#include "interfacefifo.h"	/** inclusione per comunicare con le fifo */

#include "../../CHandler/inputHandler.h" /** libreria per definire e aggiungere il filtro */
#include "../../logproxy/logproxy.h"	 /** libreria per il logging */

#define FILENAME_FIFOIN "in.fifo"
#define FILENAME_FIFOOUT "out.fifo"
#define FILENAME_FIFOLISTENER "listener.fifo"

#define MAX_LENGHT_DIR 255
#define MAX_SIZE_BUFFER BUFSIZ
#define NEWSESSION_STR "newsession"

static ConfigurationComm _MngFifo_config;

static InterfaceFifo listenerFifo;

static int _MngFifo_keySession = 0;

/**
 * @brief Definisce una struttura per impostare i parametri degli ElaborationEntity per le fifo.
 *
 * @see ElaborationEntity l'attributo <code>HiddenType params</code> specifica i parametri dell'entità.
 */
typedef struct paramsEntity {
	char pathtodir[MAX_LENGHT_DIR];
	int pid;
} ParamsEntity;

/**
 * @brief Struttura per impostare i parametri delle comunicazioni su fifo.
 *
 * @see HostCommInstance
 */
typedef struct paramsComm {
	InterfaceFifo infifo;
	InterfaceFifo outfifo;
} ParamsComm;

/**
 * @brief Funzione che restituisce il file descriptor per una sessione fifo.
 *
 * @param data puntatore ai dati riguardanti l'instaurazione della sessione.
 * @return Restituisce il file descriptor in lettura della sessione aperta.
 */
int MngFifo_getFdRead(HiddenType* data){
	ParamsComm* info = (ParamsComm*) (data->hiddentype);
	return info->infifo.pipefd;
}

/**
 * @brief Restituisce il file descriptor in scrittura per una sessione fifo.
 *
 * @see MngFifo_getRead() corrispettiva funzione per il file descriptor in lettura.
 * @param data puntatore ai dati relativi alla sessione.
 * @return l'intero rappresentante il file descriptor in scrittura della fifo.
 */
int MngFifo_getFdWrite(HiddenType* data){
	ParamsComm* info = (ParamsComm*) data->hiddentype;
	return info->outfifo.pipefd;
}

/**
 * @brief Chiude e libera la memoria allocata per la sessione fifo.
 *
 * @param data struttura contenente le informazioni sulla sessione fifo.
 */
void MngFifo_close(HiddenType*data){
	ParamsComm* info = (ParamsComm*) (data->hiddentype);
	InterfaceFifo_closeComm(&(info->infifo));
	InterfaceFifo_closeComm(&(info->outfifo));
}

void MngFifo_configure(ConfigurationComm * config){
	memcpy(&_MngFifo_config, config, sizeof(ConfigurationComm));
}

/**
 * @brief Funzione che si occupa di aprire delle sessioni fifo.
 *
 * La funzione viene dichiarata privata poichè non deve essere richiamata
 * esternamente ma solo dalle funzioni interfaccia della stessa factory
 * delle comunicazioni con l'utente.
 *
 * @note Vengono aperte due fifo, una in scrittura l'altra in lettura.
 * Precedentemente si aprivano le fifo nella corrispettiva modalità, ma
 * una fifo aperta in lettura blocca l'esecuzione finchè qualche altro processo
 * apre la fifo in scrittura.
 *
 * @param session struttura dati nella quale registrare i dati creati per la nuova sessione.
 * @param pid intero rappresentante il process identificator della nuova sessione.
 */
void _MngFifo_openSession(HostSession* session,int pid){
	ParamsComm infoChannel;
	// ricava i nomi dei file dove creare la fifo
	sprintf(infoChannel.infifo.name, "%d-%s", pid, FILENAME_FIFOIN);
	sprintf(infoChannel.outfifo.name, "%d-%s", pid, FILENAME_FIFOOUT);

    // cerca le fifo se non esistono le crea
    if(InterfaceFifo_createFifo(infoChannel.infifo.name)!=-1){
    	log_namedlevel(LOG_FIFO_NAME,LOG_INFO_LEVEL,
    			"Registra una nuova sessione");
		infoChannel.infifo.openmode = O_RDWR;//ONLY;
    } else {
		log_namedlevel(LOG_FIFO_NAME, LOG_ERROR_LEVEL,
				"Impossibile creare la fifo in input.");
		return;
    }

    if(InterfaceFifo_createFifo(infoChannel.outfifo.name)!=-1){
		infoChannel.outfifo.openmode = O_RDWR;//WRONLY;
    } else {
		log_namedlevel(LOG_FIFO_NAME, LOG_ERROR_LEVEL,
				"Impossibile creare la fifo in output.");
		return;
    }

    // prova ad aprire la fifo
    InterfaceFifo_startComm(&(infoChannel.infifo));
    InterfaceFifo_startComm(&(infoChannel.outfifo));

    // salva le operazioni svolte
    void * tmp = malloc(sizeof(ParamsComm));
    if(tmp!=NULL) {
        memcpy(tmp,&infoChannel,sizeof(ParamsComm));
		session->dataSession.hiddentype = tmp;
    }

}

/**
 * @brief Funzione che restituisce il file descriptor per il discovery di altre sessioni fifo.
 *
 * La funzione è privata poichè rientra nell'interfaccia di un oggetto @see Session
 * in particolare la funzione ricalca l'interfaccia della funzione getReadFd di questa struttura.
 *
 * @return intero che rappresenta il file descriptor da cui arriveranno le nuove richieste di sessione.
 */
int _MngFifo_getFDMain(){ return listenerFifo.pipefd;}

int _MngFifo_EmptyFn(HiddenType* data){};

/**
 * @brief Filtro per le sessioni di tipo fifo.
 *
 * La funzione rispetta i requisiti richiesti dall'InputHandler per la registrazione di nuovi filtri.
 * @see InputHandler la classe e il rispettivo ononimo file dove vengono dichiarate le interfacce per i filtri.
 *
 * Il filtro si occupa di gestire le due tipologie di sessione previste dalla tipologia di comunicazione implementata:
 * - la sessione di registrazione di nuove sessioni: collegata alla principale fifo <code>listener.fifo</code>.
 * - le sessioni create per la comunicazione con altri processi.
 *
 * @param req Puntatore alla richiesta da filtrare.
 * @return NULL se la richiesta riguarda un comando di comunicazione fifo da bloccare, la stessa richiesta passata altrimenti.
 */
UserRequest * filterFifoRequests(UserRequest * req){
	log_namedlevel(LOG_FIFO_NAME, LOG_INFO_LEVEL,
			"Filtro delle sessioni FIFO in esecuzione!");
	if(req->session->keySession == _MngFifo_keySession){
		char *rcvMsg = req->linereq;
		// decodifica
		if(strncmp(rcvMsg,NEWSESSION_STR,strlen(NEWSESSION_STR))==0){
			// avvia una nuova sessione
			log_simple("Avvia una nuova sessione FIFO");
			HostSession* newSess = malloc(sizeof(HostSession));
			Session_init(newSess,MngFifo_getFdRead,MngFifo_getFdWrite,NULL,NULL);
			// apre e assegna le fifo alla nuova sessione
			int pid = atoi(rcvMsg+strlen(NEWSESSION_STR));
			_MngFifo_openSession(newSess,pid);
			// registra una nuova sessione
			(_MngFifo_config.registerSession)(newSess);
		}
		return NULL; // interrompe la richiesta
	} else return req;
}

/**
 * @brief funzione thread che gestisce la creazione di nuove sessioni
 * di comunicazione con l'utente.
 */
void MngFifo_startListener(){
	log_namedlevel(LOG_FIFO_NAME,LOG_INFO_LEVEL,
			"Listner FIFO attivato.");
	sprintf(listenerFifo.name, FILENAME_FIFOLISTENER);
	listenerFifo.openmode = O_RDWR;//DONLY;
	// apre una fifo in lettura
	InterfaceFifo_createFifo(FILENAME_FIFOLISTENER);
	InterfaceFifo_startComm(&listenerFifo);

	log_namedlevel(LOG_FIFO_NAME,LOG_INFO_LEVEL,
			"Inserisco il filtro.");
	InputFilter fifoFilter;
	fifoFilter.filter = filterFifoRequests;
	IH_addChainFilter(&fifoFilter);

	log_namedlevel(LOG_FIFO_NAME,LOG_INFO_LEVEL,
			"Aggiungo la sessione.");
	HostSession* newSess = malloc(sizeof(HostSession));
	Session_init(newSess,_MngFifo_getFDMain,_MngFifo_EmptyFn,NULL,NULL);
	_MngFifo_keySession = newSess->keySession;
	// registra la sessione principale
	(*_MngFifo_config.registerSession)(newSess);
}

/**
 * @brief funzione pubblica che inizializza la Concrete Factory per le fifo.
 */
void MngFifo_getFactoryDesc(ConcreteFactoryComm* factdesc){
	factdesc->configure = MngFifo_configure;
	factdesc->startListen = MngFifo_startListener;

	factdesc->getReadFD = MngFifo_getFdRead;
	factdesc->getWriteFD = MngFifo_getFdWrite;
	factdesc->close = MngFifo_close;
}
